// FFEncodeFilter.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include "FFEncodeFilter.h"
#include "FFCore.h"
#include <InitGuid.h>
#include "mqguid.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//! CFFEncodeFilter
/*!
*/
CFFEncodeFilter::CFFEncodeFilter(LPUNKNOWN lpunk, HRESULT* phr)
	:	CBaseFilter(MY_FILTER_NAME, lpunk, this, CLSID_XFFENCODER)	
{
	if(phr) phr = S_OK;
	m_pSeekThrough = NULL;
	m_pInputAudioPin = new CFFEncodeInputPin(this, this, phr, L"Video In", MEDIA_TYPE_VIDEO );
	m_pInputVideoPin = new CFFEncodeInputPin(this, this, phr, L"Audio In", MEDIA_TYPE_AUDIO );
	if(!m_pInputVideoPin || !m_pInputVideoPin) {
		*phr = E_OUTOFMEMORY;
		return;
	}
	av_register_all();		
	m_pSetting = GetProfile(IPOD);
}


CFFEncodeFilter::~CFFEncodeFilter()
{
	CAutoLock lock(this);
	DELETE_POINTER(m_pInputAudioPin);
	DELETE_POINTER(m_pInputVideoPin); 
	DELETE_POINTER(m_pSeekThrough);      
}

STDMETHODIMP CFFEncodeFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
	CheckPointer(ppv, E_POINTER);
	*ppv = 0;

	if(riid == IID_IMediaSeeking || riid == IID_IMediaPosition)	{
		if(!m_pSeekThrough)	{
			HRESULT hr = S_OK;
			m_pSeekThrough = new CMyPosPassThru(_T("CantonDSSeeker"), GetOwner(), &hr, m_pInputVideoPin, m_pInputAudioPin);
			if(!m_pSeekThrough) 
				return E_OUTOFMEMORY;
			if(FAILED(hr)){
				DELETE_POINTER(m_pSeekThrough);
				return hr;
			}
		}
		return m_pSeekThrough->NonDelegatingQueryInterface(riid, ppv);
	}

	return
		QI(IAMFilterMiscFlags)			
		//QI(IFFEncodeFilter)				
		CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}


int CFFEncodeFilter::GetPinCount()
{
	return 2;
}

CBasePin* CFFEncodeFilter::GetPin(int n)
{
	if(n == 1)return m_pInputAudioPin;
	else if(n == 0) return m_pInputVideoPin;
	return NULL;	
}

STDMETHODIMP CFFEncodeFilter::Run(REFERENCE_TIME tStart)
{
	CAutoLock cLock(this);
	HRESULT hr = S_OK;
	hr = CBaseFilter::Run(tStart);
	if(FAILED(hr)) return hr;
	CallWorker(CMD_RUN);
	return S_OK;
}

STDMETHODIMP CFFEncodeFilter::Pause()
{
	CAutoLock cLock(this);

	FILTER_STATE fs = m_State;
	HRESULT hr = __super::Pause();
	if(FAILED(hr)) return hr;

	if(fs == State_Stopped){//hack
		CAMThread::Create();
		CallWorker(CMD_PAUSE);
	}
	return hr;
}

STDMETHODIMP CFFEncodeFilter::Stop()
{	
	CAutoLock cLock(this);
	HRESULT hr = __super::Stop();
	if( FAILED(hr) ) return hr;
	
	CallWorker(CMD_EXIT);
	return hr;
}

ULONG CFFEncodeFilter::GetMiscFlags()
{
	return AM_FILTER_MISC_FLAGS_IS_RENDERER;
}

COptionParameter* CFFEncodeFilter::getSetting()
{
	return m_pSetting;
}

void CFFEncodeFilter::NotifyComplete()
{
	NotifyEvent(EC_COMPLETE, S_OK, NULL);	
}

void CFFEncodeFilter::NotifyError(void* pError)
{
	NotifyEvent(EC_ERRORABORT, LONG_PTR(pError), NULL);
}

void CFFEncodeFilter::QueryActiveMediaBuffer()
{
	if( !m_qAvtiveBuffer.empty() ) m_qAvtiveBuffer.clear();

	if( m_pInputAudioPin->IsConnected() ) m_qAvtiveBuffer.push_back(m_pInputAudioPin->m_pMediaBuffer);
	if( m_pInputVideoPin->IsConnected() ) m_qAvtiveBuffer.push_back(m_pInputVideoPin->m_pMediaBuffer);
}

bool CFFEncodeFilter::GetFirstFrame(CMediaFrame** pFrame, CMediaBuffer** pBuffer)
{
	//if( !(*pBuffer) || !(*pFrame) ) return false;
	std::deque<CMediaBuffer*>::iterator it;
	LONGLONG minRef = 0x7fffffffffffffff;//max of int64	         
	CMediaBuffer* pminBuffer  = NULL;
	LONGLONG start, stop;
	for (it = m_qAvtiveBuffer.begin(); it != m_qAvtiveBuffer.end(); it++)
	{		
		(*it)->GetFirstFrameTime(start, stop);
		if(start < minRef){
			minRef = start;
			pminBuffer = *it;
		}	
	}
	if(pminBuffer){
		*pFrame = pminBuffer->GetValidFrame();
		*pBuffer = pminBuffer;
		return true;
	}
	return false;

}

void CFFEncodeFilter::RemoveActiveMediaBuffer(CMediaBuffer* pBuffer)
{
	std::deque<CMediaBuffer*>::iterator it, it2;
	for (it = m_qAvtiveBuffer.begin(); it != m_qAvtiveBuffer.end(); it++)
	{
		if(pBuffer == *it)
		{
			it2 = it;
			break;
		}
	}
	m_qAvtiveBuffer.erase(it2);
}



DWORD CFFEncodeFilter::ThreadProc()
{	
	if(m_pSeekThrough)m_pSeekThrough->ResetMediaTime();

	
	SetFileName(L"c:\\test.mp4", NULL);
	while (1)
	{
		DWORD cmd = GetRequest();
		switch(cmd)
		{
		case CMD_PAUSE:
			Reply(S_OK);
			Sleep(100);		
			break;

		case CMD_EXIT:			
		default:
			Reply(S_OK);
			m_hThread = NULL;
			return 0;

		case CMD_RUN:
			Reply(S_OK);	

			QueryActiveMediaBuffer();
			CFFCore ffmpeg;
			try
			{		
				while(m_State == State_Paused) {Sleep(500); continue;}	

				ffmpeg.CreateAudioStream(m_pInputAudioPin->IsConnected());
				ffmpeg.CreateVideoStream(m_pInputVideoPin->IsConnected());
				ffmpeg.SetSink(GetCurFile());
				ffmpeg.SetOption(m_pSetting);
				ffmpeg.SetSrcProp(&m_srcMediaProp);
				ffmpeg.InitFfmpeg();


				CMediaFrame* pEFrame = NULL;
				CMediaBuffer* pEBuffer = NULL;
				while (!CheckRequest(NULL) && !m_qAvtiveBuffer.empty())
				{	
					Sleep(1);
					if(GetFirstFrame(&pEFrame, &pEBuffer))
					{
						if(!pEFrame) continue;
						ffmpeg.EncodeFrame(pEFrame);
						if(pEFrame->GetEOS()){
							RemoveActiveMediaBuffer(pEBuffer);
						}
						pEBuffer->PushNullFrame(pEFrame);
					}
					
				}				
			}
			catch (char* err)
			{				
				TRACE("%s\n", err);
				ASSERT(0);				
				break;
			}
			ffmpeg.WriteEnd();

			if(m_pSeekThrough) m_pSeekThrough->EOS();
			//NotifyComplete();
			break;
		}
	}
	CAMThread::m_hThread = NULL;
	return 0;

}
